package com.tangula.android.http

import android.util.Log
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import okhttp3.*
import org.apache.commons.lang3.StringUtils
import java.io.IOException
import java.lang.reflect.Modifier


/**
 * Http工具类.
 * <p>使用这个类时，请继承这个类.类中的方法都是<code>protected</code>.这意味着必须对本类进行业务封装，否则不能使用其提供的Http访问接口</p>
 */
 abstract class HttpBase {


    @Suppress("UNCHECKED_CAST")
    companion object {


        @JvmStatic
        val MEDIA_JSON = MediaType.parse("application/json; charset=utf-8")

        @JvmStatic
        val METHOD_GET = { url: String, param: Any? ->
            val b = HttpUrl.get(url).newBuilder()
            for (prop in (param ?: object {}).javaClass.declaredMethods) {
                val name = prop.name
                if (StringUtils.startsWith(prop.name, "get") && prop.modifiers and Modifier.PUBLIC > 0) {
                    val value: Any? = prop.invoke(param)
                    if (value != null) {
                        b.addQueryParameter(name, value.toString())
                    }
                }
            }

            val req = Request.Builder().url(b.build())
            req.get().build()
        }

        @JvmStatic
        val METHOD_POST_JSON = { url: String, param: Any? ->
            val req = Request.Builder().url(url)
            val body = RequestBody.create(MEDIA_JSON, JSON.toJSONString(param ?: object {}))
            req.post(body).build()
        }

        @JvmStatic
        val METHOD_PUT_JSON = { url: String, param: Any? ->
            val req = Request.Builder().url(url)
            val body = RequestBody.create(MEDIA_JSON, JSON.toJSONString(param ?: object {}))
            req.put(body).build()
        }

        @JvmStatic
        val METHOD_DELETE_JSON = { url: String, param: Any? ->
            val req = Request.Builder().url(url)
            val body = RequestBody.create(MEDIA_JSON, JSON.toJSONString(param ?: object {}))
            req.delete(body).build()
        }

        protected val METHOD_COMMON_HTTP_FAIL_RESP : (Response, Call) -> Unit = {res:Response, call:Call->
            Log.e("http", res.toString())
        }

        protected val METHOD_COMMON_HTTP_FAIL : (IOException, Call) -> Unit = {ex:IOException, call:Call->

            Log.e("http", ex.localizedMessage, ex)
        }

        protected val METHOD_COMMON_BIZ_FAIL: (BizResponse<Any>?, Response, Call) -> Unit = {
            biz: BizResponse<Any>?, resp:Response, call:Call ->
            Log.e("http", resp.toString())
        }

        @JvmStatic
        protected fun <T, R> getBizSucessA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit) {
            getBizHttpSucessA(url, param, respType, onSuccess, METHOD_COMMON_BIZ_FAIL as (BizResponse<R>?, Response, Call) -> Unit)
        }

        @JvmStatic
        protected fun <T, R> postBizSucessA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit) {
            postBizHttpSucessA(url, param, respType, onSuccess, METHOD_COMMON_BIZ_FAIL as (BizResponse<R>?, Response, Call) -> Unit)
        }

        @JvmStatic
        protected fun <T, R> putBizSucessA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit) {
            putBizHttpSucessA(url, param, respType, onSuccess, METHOD_COMMON_BIZ_FAIL as (BizResponse<R>?, Response, Call) -> Unit)
        }

        @JvmStatic
        protected fun <T, R> deleteBizSucessA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit) {
            deleteBizHttpSucessA(url, param, respType, onSuccess, METHOD_COMMON_BIZ_FAIL as (BizResponse<R>?, Response, Call) -> Unit)
        }


        @JvmStatic
        protected fun <T, R> getBizHttpSucessA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit, onBizFail: (BizResponse<R>?, Response, Call) -> Unit) {
            getBizA(url, param, respType, onSuccess, onBizFail, METHOD_COMMON_HTTP_FAIL_RESP, METHOD_COMMON_HTTP_FAIL)
        }

        @JvmStatic
        protected fun <T, R> postBizHttpSucessA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit, onBizFail: (BizResponse<R>?, Response, Call) -> Unit) {
            postBizA(url, param, respType, onSuccess, onBizFail, METHOD_COMMON_HTTP_FAIL_RESP, METHOD_COMMON_HTTP_FAIL)
        }

        @JvmStatic
        protected fun <T, R> putBizHttpSucessA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit, onBizFail: (BizResponse<R>?, Response, Call) -> Unit) {
            putBizA( url, param, respType, onSuccess, onBizFail,  METHOD_COMMON_HTTP_FAIL_RESP, METHOD_COMMON_HTTP_FAIL)
        }


        @JvmStatic
        protected fun <T, R> deleteBizHttpSucessA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit, onBizFail: (BizResponse<R>?, Response, Call) -> Unit) {
            deleteBizA(url, param, respType, onSuccess, onBizFail, METHOD_COMMON_HTTP_FAIL_RESP, METHOD_COMMON_HTTP_FAIL)
        }



        @JvmStatic
        protected fun <T, R> getBizA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit, onBizFail: (BizResponse<R>?, Response, Call) -> Unit,
                                     onFailResponse: (Response, Call) -> Unit, onHttpFail: (ex: IOException, call: Call) -> Unit) {
            httpBizA(HttpBase.Companion::getA, url, param, respType, onSuccess, onBizFail, onFailResponse, onHttpFail)
        }

        @JvmStatic
        protected fun <T, R> postBizA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit, onBizFail: (BizResponse<R>?, Response, Call) -> Unit,
                                      onFailResponse: (Response, Call) -> Unit, onHttpFail: (ex: IOException, call: Call) -> Unit) {
            httpBizA(HttpBase.Companion::postJsonA, url, param, respType, onSuccess, onBizFail, onFailResponse, onHttpFail)
        }

        @JvmStatic
        protected fun <T, R> putBizA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit, onBizFail: (BizResponse<R>?, Response, Call) -> Unit,
                                     onFailResponse: (Response, Call) -> Unit, onHttpFail: (ex: IOException, call: Call) -> Unit) {
            httpBizA(HttpBase.Companion::putJsonA, url, param, respType, onSuccess, onBizFail, onFailResponse, onHttpFail)
        }


        @JvmStatic
        protected fun <T, R> deleteBizA(url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit, onBizFail: (BizResponse<R>?, Response, Call) -> Unit,
                                        onFailResponse: (Response, Call) -> Unit, onHttpFail: (ex: IOException, call: Call) -> Unit) {
            httpBizA(HttpBase.Companion::deleteJsonA, url, param, respType, onSuccess, onBizFail, onFailResponse, onHttpFail)
        }


        @JvmStatic
        private fun <T, R> httpBizA(func: (url: String, param: T, respType: Class<BizResponse<JSONObject>>, onSuccess: (BizResponse<JSONObject>?, Response, Call) -> Unit,
                                           onFailResponse: (Response, Call) -> Unit, onHttpFail: (IOException, Call) -> Unit) -> Unit,
                                    url: String, param: T, respType: Class<R>, onSuccess: (R?) -> Unit, onBizFail: (BizResponse<R>?, Response, Call) -> Unit,
                                    onFailResponse: (Response, Call) -> Unit, onHttpFail: (IOException, Call) -> Unit
        ) {
            return func(url, param, BizResponse::class.java as Class<BizResponse<JSONObject>>, { biz: BizResponse<JSONObject>?, resp: Response, call: Call ->
                if (biz != null && biz.status == 0) {
                    if (biz.status == 0) {
                        onSuccess(biz.body?.toJavaObject(respType) ?: null)
                    } else {
                        onBizFail(BizResponse<R>(biz.status, biz.statusText, biz.message, null), resp, call)
                    }
                } else {
                    onFailResponse(resp, call)
                }
            }, onFailResponse, onHttpFail)
        }

        @JvmStatic
        protected fun <T, R> getA(url: String, param: T, respType: Class<R>, onSuccess: (R?, Response, Call) -> Unit,
                        onFailResponse: (Response, Call) -> Unit, onHttpFail: (IOException, Call) -> Unit) {
            sendHttpJsonARespJson(METHOD_GET, url, param, respType, onSuccess, onFailResponse, onHttpFail)
        }

        @JvmStatic
        protected fun <T, R> postJsonA(url: String, param: T, respType: Class<R>, onSuccess: (R?, Response, Call) -> Unit,
                             onFailResponse: (Response, Call) -> Unit, onHttpFail: (ex: IOException, call: Call) -> Unit) {
            sendHttpJsonARespJson(METHOD_POST_JSON, url, param, respType, onSuccess, onFailResponse, onHttpFail)
        }

        @JvmStatic
        protected fun <T, R> putJsonA(url: String, param: T, respType: Class<R>, onSuccess: (R?, Response, Call) -> Unit,
                            onFailResponse: (Response, Call) -> Unit, onHttpFail: (ex: IOException, call: Call) -> Unit) {
            sendHttpJsonARespJson(METHOD_PUT_JSON, url, param, respType, onSuccess, onFailResponse, onHttpFail)
        }

        @JvmStatic
        protected fun <T, R> deleteJsonA(url: String, param: T, respType: Class<R>, onSuccess: (R?, Response, Call) -> Unit,
                               onFailResponse: (Response, Call) -> Unit, onHttpFail: (ex: IOException, call: Call) -> Unit) {
            sendHttpJsonARespJson(METHOD_DELETE_JSON, url, param, respType, onSuccess, onFailResponse, onHttpFail)
        }


        @JvmStatic
        protected fun <T, R> sendHttpJsonARespJson(method: (url: String, Any?) -> Request, url: String, param: T, respType: Class<R>, onSuccess: (R?, Response, Call) -> Unit,
                                         onFailResponse: (Response, Call) -> Unit, onHttpFail: (ex: IOException, call: Call) -> Unit) {
            val client = OkHttpClient()

            client.newCall(
                    method(url, param)
            ).enqueue(
                    object : Callback {
                        override fun onFailure(call: Call, e: IOException) {
                            onHttpFail(e, call)
                        }

                        @Throws(IOException::class)
                        override fun onResponse(call: Call, response: Response) {
                            if (response.isSuccessful) {
                                val body = response.body()
                                if (body != null) {
                                    val str = body.string()
                                    val obj = JSON.parseObject(str, respType)
                                    onSuccess(obj, response, call)
                                } else {
                                    onSuccess(null, response, call)
                                }
                            } else {
                                onFailResponse(response, call)
                            }
                        }
                    }
            )
        }
    }


}